use std::io::ErrorKind;
use std::net::SocketAddr;
use std::net::UdpSocket;
use std::fs;
use std::thread;

const PQA_2100: &'static str = "SA2100";
const PQA_2200: &'static str = "SA2200";
const PQA_2300: &'static str = "SA2300";

const CMD_TYPE_CONN: i32 = 0;
const CMD_TYPE_SCR: i32 = 2;
const CMD_TYPE_BC: i32 = 3;
const CMD_TYPE_SCREEN: i32 = 4;

fn server() 
{
    let args: Vec<String> = std::env::args().collect();
    let argc = args.len();

    let mut pqa_name;
    let screen;
    if argc == 2 {
        pqa_name = args[1].clone();
    } else {
        pqa_name = String::from("SA2300");
    }

    if PQA_2100 == pqa_name {
        screen = fs::read("SCR-2100.bmp").unwrap();
    } else if PQA_2200 == pqa_name {
        screen = fs::read("SCR-2200.jpg").unwrap();
    } else if PQA_2300 == pqa_name {
        screen = fs::read("SCR-2300.bmp").unwrap();
    } else {
        screen = fs::read("SCR-2300.bmp").unwrap();
        pqa_name = String::from("SA2300");
    }
    let screen_data_len = screen.len();

    println!("Hello, {}!", pqa_name);

    let pqa_server = UdpSocket::bind("0.0.0.0:3527").unwrap();
    let mut data = [0; 2048];
    let mut size = 0;
    let mut client_addr: SocketAddr;
    loop {
        println!("Listen {:?}", pqa_server);
        let ok = pqa_server.recv_from(&mut data);
        match ok {
            Ok((_size, _addr)) => {size = _size; client_addr = _addr;},
            Err(_e) => match _e.kind() {
                ErrorKind::ConnectionReset => {/*println!("ConnectionReset: {}", _e);*/continue;},
                _ => {println!("ConnectionError: {}", _e);continue;}
            },
        }

        let data = &mut data[..size];

        let t = <i32>::from_le_bytes(data[..4].try_into().unwrap());

        match t {
            0 => {
                println!("New Client {:?}", client_addr);
                let resp: [u8; 32] = unsafe {
                    let mut result = std::mem::MaybeUninit::zeroed();
                    let dest = result.as_mut_ptr() as *mut u8;
                    let mut idx = 0;
                    std::ptr::copy_nonoverlapping(t.to_le_bytes().as_ptr(), dest, t.to_le_bytes().len());
                    idx += t.to_le_bytes().len();
                    let l: i32 = 24;
                    std::ptr::copy_nonoverlapping(l.to_le_bytes().as_ptr(), dest.add(idx), l.to_le_bytes().len());
                    idx += l.to_le_bytes().len();
                    std::ptr::copy_nonoverlapping(pqa_name.as_ptr(), dest.add(idx), pqa_name.len());

                    result.assume_init()
                };

                let ok = pqa_server.send_to(&resp, client_addr);
                match ok {
                    Ok(_o) => {},
                    Err(_e) => match _e.kind() {
                        ErrorKind::ConnectionReset => {println!("ConnectionReset: {}", _e);},
                        _ => {println!("ConnectionError: {}", _e);}
                    },
                }
            },
            1 => {
                let key = <i32>::from_le_bytes(data[8..12].try_into().unwrap());
                println!("T: {}; Key: {}", t, key);
            },
            2 => {
                if PQA_2100 == pqa_name {
                    let resp: [u8; 32] = unsafe {
                        let mut result = std::mem::MaybeUninit::zeroed();
                        let dest = result.as_mut_ptr() as *mut u8;
                        let mut idx = 0;
                        std::ptr::copy_nonoverlapping(t.to_le_bytes().as_ptr(), dest, t.to_le_bytes().len());
                        idx += t.to_le_bytes().len();
                        let l: i32 = 24;
                        std::ptr::copy_nonoverlapping(l.to_le_bytes().as_ptr(), dest.add(idx), l.to_le_bytes().len());
                        idx += l.to_le_bytes().len();
                        std::ptr::copy_nonoverlapping(pqa_name.as_ptr(), dest.add(idx), pqa_name.len());
    
                        result.assume_init()
                    };
                    println!("Screen {} {:?}", t, resp.len());

                    let ok = pqa_server.send_to(&resp, client_addr);
                    match ok {
                        Ok(_o) => {},
                        Err(_e) => match _e.kind() {
                            ErrorKind::ConnectionReset => {println!("ConnectionReset: {}", _e);},
                            _ => {println!("ConnectionError: {}", _e);}
                        },
                    }

                    continue;
                }
                let mut resp = vec![0; screen_data_len+8];
                unsafe {
                    let dest = resp.as_mut_ptr() as *mut u8;
                // let resp: [u8; 261174+8] = unsafe {
                //     let mut result = std::mem::MaybeUninit::zeroed();
                //     let dest = result.as_mut_ptr() as *mut u8;
                    let mut idx = 0;
                    let t: i32 = CMD_TYPE_SCREEN;
                    std::ptr::copy_nonoverlapping(t.to_le_bytes().as_ptr(), dest, t.to_le_bytes().len());
                    idx += t.to_le_bytes().len();
                    let l: i32 = screen_data_len as i32;
                    std::ptr::copy_nonoverlapping(l.to_le_bytes().as_ptr(), dest.add(idx), l.to_le_bytes().len());
                    idx += l.to_le_bytes().len();
                    std::ptr::copy_nonoverlapping(screen.as_ptr(), dest.add(idx), screen.len());

                    // result.assume_init()
                };
                println!("Screen {} {:?}", t, resp.len());

                let mut l_ind = 0;
                let mut r_ind = 0;
                let sz = resp.len();//261174;
                if (sz - r_ind) > 1024 {
                    r_ind = 1024;
                }

                loop {
                    let ok = pqa_server.send_to(&resp[l_ind..r_ind], client_addr);
                    match ok {
                        Ok(_o) => {},
                        Err(_e) => match _e.kind() {
                            ErrorKind::ConnectionReset => {println!("ConnectionReset: {}", _e);},
                            _ => {println!("ConnectionError: {}", _e);}
                        },
                    }

                    let micros1 = std::time::Duration::from_micros(100);
                    std::thread::sleep(micros1);
                    l_ind += 1024;
                    if l_ind >= sz {
                        break;
                    }
                    if (sz - r_ind) > 1024 {
                        r_ind += 1024;
                    } else {
                        r_ind = sz;
                    }
                }
            },
            3 => {
                println!("Broadcast");
                let resp: [u8; 32] = unsafe {
                    let mut result = std::mem::MaybeUninit::zeroed();
                    let dest = result.as_mut_ptr() as *mut u8;
                    let mut idx = 0;
                    std::ptr::copy_nonoverlapping(t.to_le_bytes().as_ptr(), dest, t.to_le_bytes().len());
                    idx += t.to_le_bytes().len();
                    let l: i32 = 24;
                    std::ptr::copy_nonoverlapping(l.to_le_bytes().as_ptr(), dest.add(idx), l.to_le_bytes().len());
                    idx += l.to_le_bytes().len();
                    std::ptr::copy_nonoverlapping(pqa_name.as_ptr(), dest.add(idx), pqa_name.len());

                    result.assume_init()
                };

                let ok = pqa_server.send_to(&resp, client_addr);
                match ok {
                    Ok(_o) => {},
                    Err(_e) => match _e.kind() {
                        ErrorKind::ConnectionReset => {println!("ConnectionReset: {}", _e);},
                        _ => {println!("ConnectionError: {}", _e);}
                    },
                }
            },
            4 => {
                let mut resp = vec![0; screen_data_len+8];
                unsafe {
                    let dest = resp.as_mut_ptr() as *mut u8;
                // let resp: [u8; 261174+8] = unsafe {
                //     let mut result = std::mem::MaybeUninit::zeroed();
                //     let dest = result.as_mut_ptr() as *mut u8;
                    let mut idx = 0;
                    let t: i32 = CMD_TYPE_SCREEN;
                    std::ptr::copy_nonoverlapping(t.to_le_bytes().as_ptr(), dest, t.to_le_bytes().len());
                    idx += t.to_le_bytes().len();
                    let l: i32 = screen_data_len as i32;
                    std::ptr::copy_nonoverlapping(l.to_le_bytes().as_ptr(), dest.add(idx), l.to_le_bytes().len());
                    idx += l.to_le_bytes().len();
                    std::ptr::copy_nonoverlapping(screen.as_ptr(), dest.add(idx), screen.len());

                    // result.assume_init()
                };
                println!("Screen {} {:?}", t, resp.len());

                let mut l_ind = 0;
                let mut r_ind = 0;
                let sz = resp.len();//261174;
                if (sz - r_ind) > 1024 {
                    r_ind = 1024;
                }

                loop {
                    let ok = pqa_server.send_to(&resp[l_ind..r_ind], client_addr);
                    match ok {
                        Ok(_o) => {},
                        Err(_e) => match _e.kind() {
                            ErrorKind::ConnectionReset => {println!("ConnectionReset: {}", _e);},
                            _ => {println!("ConnectionError: {}", _e);}
                        },
                    }

                    let micros1 = std::time::Duration::from_micros(100);
                    std::thread::sleep(micros1);
                    l_ind += 1024;
                    if l_ind >= sz {
                        break;
                    }
                    if (sz - r_ind) > 1024 {
                        r_ind += 1024;
                    } else {
                        r_ind = sz;
                    }
                }
            },
            _ => {
                println!("Unkown.");
            }
        }
    }
}

fn main() {// -> std::io::Result<()>
    let server = thread::Builder::new().name("server".to_string()).stack_size(8*1024*1024).spawn(move || {
        server();
    }).unwrap();

    server.join().unwrap();
    // loop {
    //     std::thread::sleep(std::time::Duration::from_micros(10000));
    // }
}
